package com.nl.deal;

import org.apache.log4j.Logger;

import com.nl.redis.RedisCluster;
import com.nl.util.ConfigUtils;
import com.nl.util.ConnectionManager;
import com.nl.util.DateUtils;

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.ParseException;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class TaskMonitor {

    private static final Logger logger = Logger.getLogger(TaskMonitor.class);

    private static final int INTERVAL ;
    private static final int ADDPERSEND ;
    private String redisCfg;
	private RedisCluster cluster;
    static{
        INTERVAL= Integer.parseInt(ConfigUtils.getInstance().getValue("monitorInterval"));
        ADDPERSEND=Integer.parseInt(ConfigUtils.getInstance().getValue("addPerSend"));
    }
    private ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();

    private TaskMonitor() {
        logger.info("Initialize TaskMonitor.......");
    }

    private static class SingletonHolder {
        private static final TaskMonitor instance = new TaskMonitor();
    }

    public static TaskMonitor getInstance() {
        return SingletonHolder.instance;
    }

    public void startTaskMonitor(String redisCfg) {
    	this.redisCfg = redisCfg;
        this.service.scheduleAtFixedRate(new TargetTask(), 0, INTERVAL, TimeUnit.SECONDS);
    }

    private void exceute() {
        try {
            String dealTime = getOracleDate();
            List<Map<String, Long>> list = this.gainMgrDateChange(dealTime);
            if (list.size() == 0) {
                // 总数量达到阀值要求的关闭项目,周期数据达到阀值要求的暂停项目
                Map<String, String> map = this.assembleMgrMap(checkThresholdStatus(), mgrTimeOver());
                logger.info("wait update project：" + map.toString());
                this.updateMgrToClose(map);
            } else {
                // 针对跨天的情况，重置响应部分数据量，开启项目
                this.resetPartMgrThreshold(list, dealTime);
            }
        } catch (Exception e) {
        	
            logger.error("deal data error，wait next deal···"+e.toString());
            e.printStackTrace();
        }
    }

    /**
     * 查询正常运行的项目阀值设置情况
     *
     * @return 正常运行的项目阀值配置集合
     * @throws SQLException
     */
    private List<ThresholdInfo> checkThresholdStatus() throws SQLException {
        Connection conn = null;
        ResultSet rs = null;
        PreparedStatement pres = null;
        List<ThresholdInfo> list = new ArrayList<ThresholdInfo>();
        String sql = "select a.id,a.is_threshold,a.threshold,a.cycle,a.cycle_threshold," +
                "b.total_count,b.year_count,b.quarter_count,b.month_count,b.week_count,b.day_count," +
                "b.time_count from sms_template_attr a,sms_realtime_control b,sms_mgr_info c where a.id = b.id " +
                "and a.id = c.id and a.state = 2 and c.state = 0 and a.is_threshold <> 0";
        try {
            conn = ConnectionManager.getInstance().getConnection();
            pres = conn.prepareStatement(sql);
            rs = pres.executeQuery();
            ThresholdInfo thresholdInfo;
            int percent = ADDPERSEND;
            while (rs.next()) {
            	long totalThreshold = rs.getLong("THRESHOLD");
            	long cycleThreshold= rs.getLong("CYCLE_THRESHOLD");
            	if (totalThreshold > SynchDictData.thresholdControl) {
                	totalThreshold = totalThreshold * SynchDictData.upPerLimit / 100;
                } else {
                	totalThreshold = totalThreshold * SynchDictData.lowerPerLimit / 100;
                }
                
                if (cycleThreshold > SynchDictData.thresholdControl) {
                	cycleThreshold = cycleThreshold * SynchDictData.upPerLimit / 100;
                } else {
                	cycleThreshold = cycleThreshold * SynchDictData.lowerPerLimit / 100;
                }
                thresholdInfo = new ThresholdInfo();
                thresholdInfo.setMgrId(rs.getString("ID"));
                thresholdInfo.setIsThreshold(rs.getInt("IS_THRESHOLD"));
                thresholdInfo.setTotalThreshold(totalThreshold);
                thresholdInfo.setCycle(rs.getInt("CYCLE"));
                thresholdInfo.setCycleThreshold(cycleThreshold);
                thresholdInfo.setTotalCount(rs.getLong("TOTAL_COUNT"));
                thresholdInfo.setYearCount(rs.getLong("YEAR_COUNT"));
                thresholdInfo.setQuarterCount(rs.getLong("QUARTER_COUNT"));
                thresholdInfo.setMonthCount(rs.getLong("MONTH_COUNT"));
                thresholdInfo.setWeekCount(rs.getLong("WEEK_COUNT"));
                thresholdInfo.setDayCount(rs.getLong("DAY_COUNT"));
                thresholdInfo.setTimeCount(rs.getLong("TIME_COUNT"));
                logger.debug(thresholdInfo.toString());
                list.add(thresholdInfo);
            }
        } catch (SQLException e) {
            logger.error("select sms cfg threshold error");
            e.printStackTrace();
            throw e;
        } finally {
        	try{
        		if (null != rs) {
            		rs.close();
            	}
        		
        		if (null != pres) {
        			pres.close();
            	}
        		
        		if (null != conn) {
        			conn.close();
            	}
        	} catch (Exception e) {
        		e.printStackTrace();
        	}
        }
        return list;
    }

    /**
     * 查询发送时间到期的项目编码
     *
     * @return 项目编码集合
     */
    private List<String> mgrTimeOver() {
        Connection conn = null;
        ResultSet rs = null;
        PreparedStatement pres = null;
        List<String> list = new ArrayList<String>();
        String sql = "select a.id from sms_template_attr a," +
                "sms_mgr_info b where a.id=b.id and b.state<>2 and a.end_date<=trunc(sysdate)";
        try {
            conn = ConnectionManager.getInstance().getConnection();
            pres = conn.prepareStatement(sql);
            rs = pres.executeQuery();
            while (rs.next()) {
                list.add(rs.getString("ID"));
            }
        } catch (Exception e) {
            logger.error("查询发送时间已到期的项目编码出现异常");
            e.printStackTrace();
        } finally {
        	try{
        		if (null != rs) {
            		rs.close();
            	}
        		
        		if (null != pres) {
        			pres.close();
            	}
        		
        		if (null != conn) {
        			conn.close();
            	}
        	} catch (Exception e) {
        		e.printStackTrace();
        	}
        }
        return list;
    }

    /**
     * 更新项目状态
     *
     * @param map 项目编码和状态集合
     * @throws SQLException
     */
    private void updateMgrToClose(Map<String, String> map) throws SQLException {
        
    	Connection conn = null;
        PreparedStatement pres = null;
        String sql = "update sms_mgr_info t set t.state = ? , pausedate=sysdate where t.id = ?";
        try {
            conn = ConnectionManager.getInstance().getConnection();
            pres = conn.prepareStatement(sql);
            for (String mgrId : map.keySet()) {
                pres.setString(1, map.get(mgrId));
                pres.setString(2, mgrId);
                logger.info("项目修改:"+mgrId+map.get(mgrId));
                pres.addBatch();
            }
            pres.executeBatch();
        } catch (SQLException e) {
            logger.error("更新项目状态时出现异常");
            e.printStackTrace();
            throw e;
        } finally {
        	try{
        		if (null != pres) {
        			pres.close();
            	}
        		
        		if (null != conn) {
        			conn.close();
            	}
        	} catch (Exception e) {
        		e.printStackTrace();
        	}
        }
        
        
        
    }

    /**
     * 拼装需改变状态的项目和状态类型
     *
     * @param thresholdList 到达阀值的项目信息
     * @param overtimeList  发送时间过期的项目信息
     * @return 项目编码和项目状态集合
     */
    private Map<String, String> assembleMgrMap(List<ThresholdInfo> thresholdList, List<String> overtimeList) {
    	Map<String, String> map = new HashMap<String, String>();
    	try {
    		cluster=new RedisCluster(redisCfg);
    		for (String mgrId : overtimeList) {
    			map.put(mgrId, "2");
    		}
    		for (ThresholdInfo tInfo : thresholdList) {
    			//        	Map<String, String> mgrRealSend =cluster.hgetAll(tInfo.getMgrId());
    			//        	String timeC=mgrRealSend.get("timeCount");
    			//			String dayC=mgrRealSend.get("dayCount");
    			//			String weekC=mgrRealSend.get("weekCount");
    			//			String monthC=mgrRealSend.get("monthCount");
    			//			String quarterC=mgrRealSend.get("quarterCount");
    			//			String yearC=mgrRealSend.get("yearCount");
    			//			String totalC=mgrRealSend.get("totalCount");
    			//			String keyName=tInfo.getMgrId()+"hy";
    			String timeC=cluster.get(tInfo.getMgrId()+"timeCount");
    			String dayC=cluster.get(tInfo.getMgrId()+"dayCount");
    			String weekC=cluster.get(tInfo.getMgrId()+"weekCount");
    			String monthC=cluster.get(tInfo.getMgrId()+"monthCount");
    			String quarterC=cluster.get(tInfo.getMgrId()+"quarterCount");
    			String yearC=cluster.get(tInfo.getMgrId()+"yearCount");
    			String totalC=cluster.get(tInfo.getMgrId()+"totalCount");

    			if (null == timeC || "".equals(timeC)) {
    				timeC = "0";
    			}
    			if (null == dayC || "".equals(dayC)) {
    				dayC = "0";
    			}
    			if (null == weekC || "".equals(weekC)) {
    				weekC = "0";
    			}
    			if (null == monthC || "".equals(monthC)) {
    				monthC = "0";
    			}
    			if (null == quarterC || "".equals(quarterC)) {
    				quarterC = "0";
    			}
    			if (null == yearC || "".equals(yearC)) {
    				yearC = "0";
    			}
    			if (null == totalC || "".equals(totalC)) {
    				totalC = "0";
    			}
    			int timeCount = Integer.valueOf(timeC);
    			int dayCount = Integer.valueOf(dayC);
    			int weekCount = Integer.valueOf(weekC);
    			int monthCount = Integer.valueOf(monthC);
    			int quarterCount = Integer.valueOf(quarterC);
    			int yearCount = Integer.valueOf(yearC);
    			int totalCount = Integer.valueOf(totalC);
    			if (tInfo.getIsThreshold() == 2 || tInfo.getIsThreshold() == 3) {
    				if (tInfo.getCycle() == 1 && (yearCount - tInfo.getCycleThreshold() >= 0)
    						|| tInfo.getCycle() == 2 && (monthCount - tInfo.getCycleThreshold() >= 0)
    						|| tInfo.getCycle() == 3 && (weekCount - tInfo.getCycleThreshold() >= 0)
    						|| tInfo.getCycle() == 4 && (dayCount - tInfo.getCycleThreshold() >= 0)
    						|| tInfo.getCycle() == 5 && (timeCount - tInfo.getCycleThreshold() >= 0)
    						|| tInfo.getCycle() == 6 && (quarterCount - tInfo.getCycleThreshold() >= 0)
    						) {
    					map.put(tInfo.getMgrId(), "1");
    				}
    			}
    			if ((tInfo.getIsThreshold() == 1 || tInfo.getIsThreshold() == 3)
    					&& (totalCount - tInfo.getTotalThreshold() >= 0)) {
    				map.put(tInfo.getMgrId(), "2");
    			}
    		}
    	} catch (IOException e) {
    		try {
    			cluster.close();
    		} catch (IOException e1) {
    			e1.printStackTrace();
    		}
    		e.printStackTrace();
    		logger.info("获取redis实例异常"+e.toString());
    	}
    	finally {
    		try {
        		if(null != cluster){
        			cluster.close();
        		}
        	} catch (IOException e) {
        		e.printStackTrace();
        	}
		}
    	return map;
    }

    /**
     * 针对跨天的情况，重置响应部分数据量，开启项目
     *
     * @param list     待处理项目集合
     * @param dealTime 数据库时间
     * @throws Exception
     */
    private void resetPartMgrThreshold(List<Map<String, Long>> list, String dealTime) throws Exception {
        Connection conn = null;
        PreparedStatement pres1 = null;
        PreparedStatement pres2 = null;
        String sql1 = this.assembleSql(dealTime);
        String sql2 = "update sms_mgr_info t set t.state = 0  , opendate=sysdate where t.id = ?";
        logger.info("resetPartMgrThreshold sql:" + sql1);
        try {
            conn = ConnectionManager.getInstance().getConnection();
            conn.setAutoCommit(false);
            pres1 = conn.prepareStatement(sql1);
            pres1.executeUpdate();
            pres2 = conn.prepareStatement(sql2);
            for (Map<String, Long> map : list) {
                logger.info("待开启的项目：" + map.toString());
                if (judgeDateConform(map.get("cycle"), dealTime, map)) {
                    pres2.setLong(1, map.get("id"));
                    pres2.addBatch();
                }
            }
            pres2.executeBatch();
            conn.commit();
        } catch (SQLException e) {
            logger.error("更新项目状态和项目的数据量出现异常");
            e.printStackTrace();
            throw e;
        } finally {
        	try{
        		if (null != pres1) {
            		pres1.close();
            	}
        	
        		if (null != pres2) {
        			pres2.close();
            	}
        		
        		if (null != conn) {
        			conn.close();
            	}
        	} catch (Exception e) {
        		e.printStackTrace();
        	}
        }
    }

    /**
     * 判断是否需要开启项目
     *
     * @param cycle    周期阀值类型
     * @param dealTime 数据库时间
     * @return 是否满足条件
     * @throws ParseException
     */
    private boolean judgeDateConform(long cycle, String dealTime, Map<String, Long> map) throws ParseException {
        boolean flag = false;
        Date date = DateUtils.formatDate(dealTime);
        if (cycle == 4) {
            flag = true;
            logger.info("跨日开启项目" + map.get("id"));
        } else if (cycle == 3 && DateUtils.sameDate(date,DateUtils.getFirstDayOfWeek(date))) {
            flag = true;
            logger.info("跨周开启项目" + map.get("id"));
        } else if (cycle == 2 && DateUtils.sameDate(date,DateUtils.getFirstDayOfMonth(date))) {
            flag = true;
            logger.info("跨月开启项目" + map.get("id"));
        } else if (cycle == 1 && DateUtils.sameDate(date,DateUtils.getCurrYearFirst())) {
            flag = true;
            logger.info("跨年开启项目" + map.get("id"));
        } else if (cycle == 6 && DateUtils.sameDate(date,DateUtils.getFirstDayOfQuarter(date))) {
            flag = true;
            logger.info("跨季度开启项目" + map.get("id"));
        }
        return flag;
    }

    /**
     * 拼装更新实时提交量的sql，重置提交量
     *
     * @param dealTime 数据库时间
     * @return sql
     * @throws ParseException
     */
    private String assembleSql(String dealTime) throws ParseException {
        StringBuffer buffer = new StringBuffer();
        buffer.append("update sms_realtime_control t set t.day_count = 0");
        Boolean rsetRealsend = this.rsetRealsend("dayCount","0");
        Date date = DateUtils.formatDate(dealTime);
        if (DateUtils.sameDate(date, DateUtils.getFirstDayOfWeek(date))) {
            buffer.append(",t.week_count = 0");
            this.rsetRealsend("weekCount","0");
        }
        if (DateUtils.sameDate(date,DateUtils.getFirstDayOfMonth(date))) {
            buffer.append(",t.month_count = 0");
            this.rsetRealsend("monthCount","0");
        }
        if (DateUtils.sameDate(date, DateUtils.getFirstDayOfQuarter(date))) {
            buffer.append(",t.quarter_count = 0");
            this.rsetRealsend("quarterCount","0");
        }
        if (DateUtils.sameDate(date,DateUtils.getCurrYearFirst())) {
            buffer.append(",t.year_count = 0");
            this.rsetRealsend("yearCount","0");
        }
        buffer.append(",t.flag_time = sysdate");//跨天所有的都重置
        return buffer.toString();
    }

    /**
     * 查询非关闭、模板审核通过、且设置了周期阀值的项目是否跨天
     *
     * @param dealTime 数据库时间
     * @throws SQLException
     */
    private List<Map<String, Long>> gainMgrDateChange(String dealTime) throws SQLException {
        Connection conn = null;
        ResultSet rs = null;
        PreparedStatement pres = null;
        List<Map<String, Long>> list = new ArrayList<Map<String, Long>>();
        String sql = "select a.id,a.cycle from sms_template_attr a, sms_realtime_control b, sms_mgr_info c " +
                "where a.id = b.id and a.id = c.id and a.state = 2 and c.state in (0,1) and a.is_threshold " +
                "in (2, 3) and a.cycle <> 5 and trunc(b.flag_time) <> to_date(?,'yyyyMMdd')";
        logger.debug("gainMgrDateChange sql:" + sql);
        try {
            conn = ConnectionManager.getInstance().getConnection();
            pres = conn.prepareStatement(sql);
            pres.setString(1, dealTime.substring(0, 8));
            rs = pres.executeQuery();
            Map<String, Long> map;
            while (rs.next()) {
                map = new HashMap<String, Long>();
                map.put("id", rs.getLong("ID"));
                map.put("cycle", rs.getLong("CYCLE"));
                logger.debug("跨天开启的项目：" + map.toString());
                list.add(map);
            }
        } catch (SQLException e) {
            logger.error("查询非关闭、模板审核通过、且设置了周期阀值的跨天项目出现异常");
            e.printStackTrace();
            throw e;
        } finally {
        	try{
        		if (null != rs) {
            		rs.close();
            	}
        		
        		if (null != pres) {
        			pres.close();
            	}
        		
        		if (null != conn) {
        			conn.close();
            	}
        	} catch (Exception e) {
        		e.printStackTrace();
        	}
        }
        return list;
    }

    /**
	 * 获取数据库时间
	 * @throws Exception 
	 */
	private String getOracleDate() throws Exception{
		String strTime = "";
		Connection conn = null;
		ResultSet rs = null;
		PreparedStatement pres = null;
		String sql = "select to_char(sysdate,'yyyyMMdd hh24:mi:ss') tim from dual";
		try {
			conn = ConnectionManager.getInstance().getConnection();
			pres = conn.prepareStatement(sql);
			rs = pres.executeQuery();
			if(rs.next()){
				strTime = rs.getString("TIM");
			}
		} catch (Exception e) {
			logger.error("查询数据库时间出现异常···");
			throw e;
		} finally {
			try{
        		if (null != rs) {
            		rs.close();
            	}
        		
        		if (null != pres) {
        			pres.close();
            	}
        		
        		if (null != conn) {
        			conn.close();
            	}
        	} catch (Exception e) {
        		e.printStackTrace();
        	}
		}
		return strTime;
	}
	
    private class TargetTask implements Runnable {

        @Override
        public void run() {
            exceute();
            logger.info("状态控制线程开始执行，时间：" + new Date());
        }
    }
    
    private Boolean rsetRealsend(String thresoldName,String num){
    	boolean result = false;
    	Connection conn = null;
		ResultSet rs = null;
		PreparedStatement pres = null;
		String mgrId="";
		String sql = "select id from sms_realtime_control ";
		int reSetMgrcount=0;    	
    	try {
    		conn = ConnectionManager.getInstance().getConnection();
			pres = conn.prepareStatement(sql);
			rs = pres.executeQuery();
			cluster=new RedisCluster(redisCfg);	 
			while(rs.next()){
				mgrId = rs.getString("id");
		        String result1=cluster.set(mgrId+thresoldName, num);
		        logger.info("跨周期项目发送量重置成功:"+result1+"，项目编码："+mgrId);
		        reSetMgrcount++;
			} 
			logger.info("完成发送量重置的跨周期项目数量："+reSetMgrcount);
			result = true;
		} catch (Exception e) {
	        logger.error("跨周期项目发送量重置出现异常，项目编码："+mgrId+";error:"+e.toString());
			e.printStackTrace();
		}finally {
			try{
				if(null != cluster){
		        	cluster.close();
		        }
        		if (null != rs) {
            		rs.close();
            	}       		
        		if (null != pres) {
        			pres.close();
            	}
        		if (null != conn) {
        			conn.close();
            	}
        	} catch (Exception e) {
        		logger.error("close--error:"+e.toString());
        		e.printStackTrace();
        	}
		}
    	return result;
    	
    }
    
    
}
